#!/usr/bin/env python
# -*- coding:utf8 -*-
# uncompyle6 version 2.15.1
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.10 (default, Jul  1 2017, 13:36:56) 
# [GCC 4.4.6 20110731 (Red Hat 4.4.6-4)]
# Embedded file name: ./paas/saas/views.py
# Compiled at: 2017-11-16 15:44:28
"""
Tencent is pleased to support the open source community by making 蓝鲸智云(BlueKing) available.
Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
"""
import yaml
from django.db.models import Q
from django.conf import settings
from django.http import HttpResponseRedirect
from app.constants import AppStateEnum
from common.log import logger
from common.mymako import render_json, render_mako_context, render_mako_tostring_context
from common.utils import record_user_operate
from common.decorators import is_saas_app_exist, is_superuser
from engine.models import BkServer
from engine.deploy import saas_app_to_online_task
from saas.models import SaaSUploadFile, SaaSAppVersion, SaaSApp
from saas.utils import save_saas_app_info, validate_and_extract_tar_file, extract_logo_file, create_app, update_app, md5_for_file, upload_response_tpl, delete_saas_app, get_app_cur_version_info, is_saas_app_already_exist
from release.constants import UserOperateTypeEnum
from release.utils import update_app_state

@is_superuser
def saas_list(request):
    """
    SaaS 列表
    """
    return render_mako_context(request, 'saas/list.html')


@is_superuser
def query_app_list(request):
    """
    查询获得上传部署应用的列表
    """
    keyword = request.GET.get('keyword').replace('&nbsp;', '').strip()
    try:
        hide_outline = int(request.GET.get('hide_outline', '0'))
        page = int(request.GET.get('page', 1))
        page_size = int(request.GET.get('page_size', 8))
    except Exception as e:
        logger.exception(u'应用列表页面参数异常:%s' % e)
        return render_json({'data': u'请求参数异常',
           'total_num': 0,
           'extend_fun': ''
           })

    start = (page - 1) * page_size
    end = page * page_size
    app_all_list = SaaSApp.objects.all().order_by('-created_time')
    if keyword:
        app_all_list = app_all_list.filter(Q(name__icontains=keyword) | Q(code__icontains=keyword))
    if hide_outline == 0:
        app_all_list = app_all_list.exclude(app__state=0)
    total = app_all_list.count()
    app_list = app_all_list[start:end]
    refresh_app_list = []
    app_refresh_states = [
     AppStateEnum.IN_TEST, AppStateEnum.IN_ONLINE, AppStateEnum.IN_OUTLINE]
    for _app in app_list:
        if _app.state in app_refresh_states:
            _app_code = _app.code
            try:
                update_app_state(_app_code)
                _app = SaaSApp.objects.get(code=_app_code)
            except:
                logger.exception(u'更新应用[%s]状态失败' % _app_code)

        refresh_app_list.append(_app)

    result = {'total': total,
       'app_list': app_list
       }
    template_name = 'saas/list_table.part'
    html_data = render_mako_tostring_context(request, template_name, result)
    return render_json({'data': html_data,
       'total_num': total,
       'extend_fun': ''
       })


@is_saas_app_exist
def info(request, app_code):
    """
    SaaS 应用基本信息
    """
    saas_app = SaaSApp.objects.get(code=app_code)
    app = saas_app.app
    app_state = saas_app.state
    app_info = {'code': saas_app.code or '--',
       'name': saas_app.name or '--',
       'introduction': app.introduction if app else '',
       'current_version': saas_app.current_version,
       'create_time': saas_app.created_date_display,
       'state': app_state
       }
    res, version_info = get_app_cur_version_info(saas_app)
    if res:
        app_info.update(version_info)
    return render_mako_context(request, 'saas/info.html', {'app_info': app_info,
       'app_code': app_code,
       'app_state': app_state
       })


@is_saas_app_exist
def release(request, app_code):
    """
    部署页面
    """
    app_state = ''
    version_info = {}
    if app_code != 0:
        saas_app = SaaSApp.objects.get(code=app_code)
        app_state = saas_app.state
        res, version_info = get_app_cur_version_info(saas_app)
    ret = {'app_code': app_code,'app_state': app_state,
       'version_info': version_info
       }
    return render_mako_context(request, 'saas/release.html', ret)


@is_saas_app_exist
def offline(request, app_code):
    """
    下架页面
    """
    saas_app = SaaSApp.objects.get(code=app_code)
    app_state = saas_app.state
    res, version_info = get_app_cur_version_info(saas_app)
    if not res:
        return render_mako_context(request, 'error/app_error3.html', {'app_code': app_code})
    version_info['app_code'] = app_code
    version_info['app_state'] = app_state
    return render_mako_context(request, 'saas/offline.html', version_info)


@is_saas_app_exist
def delete(request, app_code):
    """
    删除某个 saas 及其所有版本
    """
    username = request.user.username
    res, msg = delete_saas_app(app_code, username)
    return render_json({'result': res,'msg': msg})


@is_saas_app_exist
def record(request, app_code):
    """
    应用发布记录
    """
    saas_app = SaaSApp.objects.get(code=app_code)
    app_state = saas_app.state
    return render_mako_context(request, 'saas/record.html', {'app_code': app_code,'app_state': app_state})


@is_superuser
def upload(request, app_code):
    """
    执行上传, 一整个事务操作
    
    注意安全性处理
    
    Note:
    1. app_code = 0, 则上传的新应用
    2. app_code != 0, 则上传老的应用, 需要校验app_code同包里的 app_code 是否一致
    """
    saas_file = request.FILES.get('saas_file')
    if not saas_file or not saas_file.name:
        return upload_response_tpl(False, u'上传文件失败!')
    if not saas_file.name.endswith('.tar.gz'):
        return upload_response_tpl(False, u'错误的文件! SaaS应用为.tar.gz格式')
    md5 = md5_for_file(saas_file.chunks())
    saas_upload_file = SaaSUploadFile()
    saas_upload_file.name = saas_file.name
    saas_upload_file.size = saas_file.size
    saas_upload_file.md5 = md5
    saas_upload_file.file = saas_file
    saas_upload_file.save()
    is_valid, message, app_yml_content = validate_and_extract_tar_file(filename=saas_upload_file.name, path=saas_upload_file.file.path)
    if not is_valid:
        logger.info(message)
        return upload_response_tpl(False, message)
    app_config = {}
    try:
        app_config = yaml.load(app_yml_content)
    except:
        message = u'无法正确加载%s包中的yml文件' % saas_upload_file.name
        logger.exception(message)
        return upload_response_tpl(False, message)

    saas_app_code = app_config.get('app_code')
    if app_code == '0':
        app_code = saas_app_code
        if is_saas_app_already_exist(app_code):
            message = u'上传包应用 ID 为: %s. 应用已存在, 非新应用无法从此入口部署. 请从内置应用列表找到该应用, 进入部署页面部署' % app_code
            logger.info(message)
            return upload_response_tpl(False, message)
    else:
        if app_code != saas_app_code:
            message = u'当前应用 ID 为: %s, 上传包应用 ID 为: %s. 不是同一个应用, 无法部署. 请作为新应用上传部署或找到应用%s的部署页面上传部署 ' % (
             app_code, saas_app_code, saas_app_code)
            logger.info(message)
            return upload_response_tpl(False, message)
    app_name = app_config.get('app_name')
    version = app_config.get('version')
    if not (app_code and app_name and version):
        message = 'upload file: %s, app.yml settings error [app_code=%s, app_name=%s, version=%s]' % (saas_file.name,
         app_code,
         app_name,
         version)
        logger.info(message)
        return upload_response_tpl(False, message)
    try:
        saas_app_version_id = save_saas_app_info(app_config, saas_upload_file)
    except:
        message = u'保存SaaS包信息失败'
        logger.exception(message)
        return upload_response_tpl(False, message)

    extract_logo_file(filename=saas_upload_file.name, path=saas_upload_file.file.path, saas_app_code=saas_app_code)
    file_version_display = '%s (V%s)' % (saas_file.name, version)
    data = {'saas_app_version_id': saas_app_version_id,'file_version_display': file_version_display}
    return upload_response_tpl(True, u'上传成功', data)


@is_superuser
def online(request, saas_app_version_id):
    """
    执行部署
    """
    username = request.user.username
    result = _saas_online_task(saas_app_version_id, username)
    return render_json(result)


def modify_app_logo(request, app_code):
    """
    修改应用图标
    """
    app = SaaSApp.objects.get(code=app_code)
    logo = request.FILES.get('logo_m', '')
    if not logo:
        error = '更换logo失败, logo必须为png格式'
        return HttpResponseRedirect('%ssaas/list/?error=%s' % (settings.SITE_URL, error))
    try:
        log_type = logo.content_type
        if log_type == 'image/png':
            app.logo = logo
            app.save()
    except:
        logger.exception(u'应用logo[%s]更换失败' % app_code)

    return HttpResponseRedirect('%ssaas/list/' % settings.SITE_URL)


def _saas_online_task(saas_app_version_id, username, is_back=False):
    """
    SaaS应用部署操作
    放在 utils.py 中会循环引用，所以放在 views.py 中
    """
    is_saas_app_deployable = BkServer.objects.check_saas_app_deployable()
    if not is_saas_app_deployable:
        if is_back:
            msg = u'当前没有可用的[正式服务器], 无法进行应用部署操作. 请到 [蓝鲸智云-开发者中心-服务器信息] 注册并激活服务器'
        else:
            msg = u"\u5f53\u524d\u6ca1\u6709\u53ef\u7528\u7684[\u6b63\u5f0f\u670d\u52a1\u5668], \u65e0\u6cd5\u8fdb\u884c\u5e94\u7528\u90e8\u7f72\u64cd\u4f5c. \u8bf7\u5230<a href='/engine/servers/'> [\u84dd\u9cb8\u667a\u4e91-\u5f00\u53d1\u8005\u4e2d\u5fc3-\u670d\u52a1\u5668\u4fe1\u606f] </a>\u6ce8\u518c\u5e76\u6fc0\u6d3b\u670d\u52a1\u5668"
        logger.info(msg)
        result = {'result': False,'msg': msg}
        return result
    try:
        saas_app_version = SaaSAppVersion.objects.get(id=saas_app_version_id)
    except SaaSAppVersion.DoesNotExist:
        return {'result': False,'msg': u'对应版本不存在, 无法进行部署'}

    saas_app = saas_app_version.saas_app
    app = saas_app.app
    if not app:
        is_success, message, app = create_app(saas_app_version)
        if not is_success:
            return {'result': False,'msg': message}
        saas_app.app = app
        saas_app.save()
    is_success, message = update_app(saas_app_version)
    if not is_success:
        return {'result': False,'msg': message}
    app_code = app.code
    logger.info(u'SaaS App[app:%s] 开始进行[正式部署]...' % app_code)
    if app.state not in [AppStateEnum.OUTLINE, AppStateEnum.DEVELOPMENT, AppStateEnum.TEST, AppStateEnum.ONLINE]:
        msg = u'SaaS App应用当前状态：%s，不能进行部署操作！' % app.get_state_display()
        logger.info(u'SaaS App[app:%s] %s' % (app_code, msg))
        result = {'result': False,'msg': msg}
        return result
    is_success, event_id, message = saas_app_to_online_task(saas_app_version, username)
    extra_data = {'username': username}
    record_user_operate(app_code=app_code, username=username, operate_type=UserOperateTypeEnum.RELEASE_ONLINE, extra_data=extra_data)
    if is_success:
        msg = u'SaaS App正式部署事件提交成功！'
        result = {'result': True,'msg': msg,'event_id': event_id,'app_code': app_code}
    else:
        msg = message
        result = {'result': False,'msg': msg,'event_id': event_id}
    logger.info(u'SaaS App[app:%s] %s event_id: %s' % (app_code, msg, event_id))
    return result


def deploy_app(app_code):
    """
    部署SaaS应用API
    """
    try:
        saas_app = SaaSApp.objects.get(code=app_code)
    except:
        return render_json({'result': False,'msg': u'应用[%s]数据信息未初始化' % app_code})

    res, version_info = get_app_cur_version_info(saas_app)
    saas_app_version_id = version_info.get('id')
    result = _saas_online_task(saas_app_version_id, 'admin')
    return render_json(result)
